#pragma once

#include <vex/core/handle_policy.h>
#include <vex/core/as_any.h>
#include <vex/core/as_intrusive.h>
#include <vex/functional/callable_with_invoke_policy.h>
#include <vex/functional/contract/delegate.h>
#include <vex/functional/result.h>
#include <type_traits>

namespace vex { 
    namespace functional {
        template <class TSignature>
        struct any_delegate_traits {
            // required by handle_policy:
            typedef contract::delegate<TSignature> contract_type;
            // required by handle_policy:
            typedef boost::intrusive_ptr<contract_type> holder_type;
            // required by all policies
            typedef contract_type* item_type;
            // required by callable_with_invoke_policy
            typedef typename result<TSignature>::type result_type;
        };

        template <class TSignature>
        class any_delegate : public core::handle_policy<
            any_delegate<TSignature>, 
            any_delegate_traits<TSignature>,
            callable_with_invoke_policy< any_delegate<TSignature>, any_delegate_traits<TSignature> >
        >
        {
        public:
            /// <summary>
            /// Big five implementation and common public API of <c>handle_policy</c>
            /// </summary>
            VEX_ANY_HANDLE_DEFAULT_BODY_IMPL(
                any_delegate, 
                any_delegate_traits<TSignature>,
                VEX_TRAITS_IS_DEPENDENT_NAME);
        private:
            holder_type m_holder;
        };

        /// adl support, must be in the same namespace as contract
        namespace contract {
            template<class TSignature> auto adl_as_any_from_out(delegate<TSignature>* p_handle)
                -> any_delegate<TSignature>
            {
                return any_delegate<TSignature>(p_handle, false);
            }

            template<class TSignature> auto adl_as_any_from_in(delegate<TSignature>* p_handle)
                -> any_delegate<TSignature>
            {
                return any_delegate<TSignature>(p_handle, true);
            }
        }
    }

    using functional::any_delegate;
}